/*
 * GUIBuilder.java
 *
 * Copyright 2007, 2008 Tiberiumlabs
 *
 * This file is part of Tiberiumlabs Lailaps.
 *
 * Tiberiumlabs Lailaps is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tiberiumlabs Lailaps is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.tiberiumlabs.lailaps.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.WeakHashMap;
import java.util.regex.PatternSyntaxException;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.ScrollPaneConstants;
import javax.swing.WindowConstants;
import javax.swing.border.EtchedBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import org.tiberiumlabs.lailaps.Mediator;
import org.tiberiumlabs.lailaps.actions.ActionFactory;
import org.tiberiumlabs.lailaps.actions.system.SelectColumnsAction;
import org.tiberiumlabs.lailaps.actions.system.ViewLogAction;
import org.tiberiumlabs.lailaps.gui.authorize.SimpleAuthorizationDialog;
import org.tiberiumlabs.lailaps.gui.droptarget.DropTarget;
import org.tiberiumlabs.lailaps.gui.droptarget.DroptargetStatPanel;
import org.tiberiumlabs.lailaps.gui.elements.ExceptionDialog;
import org.tiberiumlabs.lailaps.gui.elements.GCLabel;
import org.tiberiumlabs.lailaps.gui.elements.MainStatusBar;
import org.tiberiumlabs.lailaps.gui.elements.Table;
import org.tiberiumlabs.lailaps.gui.elements.TableCellRenderer;
import org.tiberiumlabs.lailaps.gui.elements.TableColumnModel;
import org.tiberiumlabs.lailaps.gui.transfer.TableTransferHandler;
import org.tiberiumlabs.lailaps.list.DownloadsListModel;
import org.tiberiumlabs.lailaps.resources.ImageLoader;
import org.tiberiumlabs.lailaps.settings.InterfaceSettings;
import org.tiberiumlabs.lailaps.settings.Settings;
import org.tiberiumlabs.swing.DesktopToolkit;

/**
 *
 * @author <a href="mailto:paranoid.tiberiumlabs@gmail.com">Paranoid</a>
 */
public abstract class GUIBuilder {

    private static final ResourceBundle bundle = ResourceBundle.getBundle("org/tiberiumlabs/lailaps/resources/messages/gui");
    private static final ResourceBundle menuBundle = ResourceBundle.getBundle("org/tiberiumlabs/lailaps/resources/messages/menu");

    // <editor-fold defaultstate="collapsed" desc=" strings to identify components in WeakHashMap ">
    // constant keys for components:
    private static final String TABLE = "downloads_table";
    private static final String JUST_TABLE = "table";
    private static final String MAIN_VIEW = "main_view";
    private static final String TOOLBAR = "toolbar";
    private static final String MAIN_MENU = "main_menu";
    private static final String POPUP_MENU = "popup_menu";
    private static final String MAIN_STATUS_BAR = "main_status_bar";
    private static final String DROP_TARGET_STATUS_BAR = "drop_target_status_bar";

    // titles and just final strings:
    private static final String MAIN_WINDOW_TITLE = "Lailaps";
    private static final String EMPTY_STRING = "";

    // </editor-fold>

    /**
     * map to hold all components. may be it will be better to do like this -
     * use some component only if we really need it...
     */
    private Map<String, JComponent> componentsMap = new WeakHashMap<String, JComponent>();
    private DownloadsListModel model = Mediator.getMediator().getDownloadsListModel();

    // <editor-fold defaultstate="collapsed" desc=" top level components ">
    private DropTarget dropTarget;
    public synchronized DropTarget getDropTarget() {
        if (dropTarget == null) {
            dropTarget = new DropTarget() {
                @Override
                public void dispose() {
                    dropTarget = null;
                    super.dispose();
                }
            };

            dropTarget.addWindowListener(Mediator.getMediator().getActionFactory().createShowDropTargetAction());

            InterfaceSettings settings = Settings.getInterfaceSettings();
            if (settings.getDroptargetWindowX() > 0 || settings.getDroptargetWindowY() > 0) {
                dropTarget.setLocation(settings.getDroptargetWindowX(), settings.getDroptargetWindowY());
            }
            if (settings.getDroptargetWindowHeight() > 0 || settings.getDroptargetWindowWidth() > 0) {
                dropTarget.setSize(settings.getDroptargetWindowWidth(), settings.getDroptargetWindowHeight());
            }

            dropTarget.setMinimumSize(new Dimension(24, 24));
        }
        return dropTarget;
    }
    public boolean isDropTargetVisible() {
        return dropTarget != null;
    }

    private JFrame mainWindow = null;
    public synchronized JFrame getMainWindow() {
        if (mainWindow == null) {
            mainWindow = new JFrame(MAIN_WINDOW_TITLE) {
                @Override
                public void dispose() {
                    mainWindow = null;
                    super.dispose();
                }
            };

            ActionFactory actionFactory = Mediator.getMediator().getActionFactory();

            mainWindow.addWindowListener(actionFactory.createShowMainWindowAction());

            mainWindow.setContentPane(getMainView());
            mainWindow.setJMenuBar(getMainMenu());

            mainWindow.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
            mainWindow.addWindowListener(actionFactory.createExitAction());
            mainWindow.pack();
            mainWindow.setMinimumSize(new Dimension(160, 160));

            InterfaceSettings settings = Settings.getInterfaceSettings();
            if (settings.getMainWindowX() > 0  || settings.getMainWindowY() > 0) {
                mainWindow.setLocation(settings.getMainWindowX(), settings.getMainWindowY());
            }
            if (settings.getMainWindowWidth() > 0  && settings.getMainWindowHeight() > 0) {
                mainWindow.setSize(settings.getMainWindowWidth(), settings.getMainWindowHeight());
            }


            // <editor-fold defaultstate="collapsed" desc=" key bindings ">
            InputMap inputMap = mainWindow.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
            ActionMap actionMap = mainWindow.getRootPane().getActionMap();

            String deleteCommandName = "window:delete";
            actionMap.put(deleteCommandName, actionFactory.createRemoveElementAction());
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), deleteCommandName);
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, KeyEvent.SHIFT_DOWN_MASK), deleteCommandName);

            String addCommandName = "window:add";
            actionMap.put(addCommandName, actionFactory.createAddElementAction());
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0), addCommandName);
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK), addCommandName);
            inputMap.put(KeyStroke.getKeyStroke("ctrl V"), addCommandName);

            String startCommandName = "window:start";
            actionMap.put(startCommandName, actionFactory.createStartAction());
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK), startCommandName);

            String pauseCommandName = "window:pause";
            actionMap.put(pauseCommandName, actionFactory.createPauseAction());
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_DOWN_MASK), pauseCommandName);

            String stopCommandName = "window:stop";
            actionMap.put(stopCommandName, actionFactory.createStopAction());
            inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_DOWN_MASK), stopCommandName);

            // </editor-fold>

        }
        return mainWindow;
    }
    public boolean isMainWindowVisible() {
        return mainWindow != null;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" internal components ">

    public JComponent getDownloadsTable() {
        Box box = (Box)componentsMap.get(TABLE);
        if (box == null) {
            final TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model);

            // create and set all parameters of table:
            final JTable jtable = getTable();
            jtable.setRowSorter(sorter);

            // <editor-fold defaultstate="collapsed" desc=" Row Numbers table ">
            RowNumTable rowNumTable = new RowNumTable(jtable);
            rowNumTable.setAutoCreateColumnsFromModel(false);
            rowNumTable.setModel(model);
            rowNumTable.setSelectionModel(jtable.getSelectionModel());
            rowNumTable.setAutoscrolls(false);
            rowNumTable.addColumn(new TableColumn());
            rowNumTable.getColumnModel().getColumn(0).setCellRenderer(jtable.getTableHeader().getDefaultRenderer());
            rowNumTable.calculateSize();
            // </editor-fold>

            Insets emptyInsets = new Insets(0, 0, 0, 0);

            // <editor-fold defaultstate="collapsed" desc=" Unsort button ">
            JButton unsortButton = new JButton();
            unsortButton.setIcon(ImageLoader.getUnsortIcon());
            unsortButton.setToolTipText(bundle.getString("unsort.tooltip"));
            unsortButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    sorter.setSortKeys(null);
                }
            });
            unsortButton.setMargin(emptyInsets);
            unsortButton.setIconTextGap(0);
            unsortButton.setFocusPainted(false);
            // </editor-fold>

            final JTextField filterField = new JTextField();
            DesktopToolkit.setMaxComponentHeight(filterField);
            filterField.setToolTipText(bundle.getString("filterfield.tooltip"));

            // <editor-fold defaultstate="collapsed" desc=" DocumentListener for filter ">
            filterField.getDocument().addDocumentListener(new DocumentListener() {
                @Override
                public void changedUpdate(DocumentEvent e) {
                    fireDocumentChanged();
                }
                @Override
                public void insertUpdate(DocumentEvent e) {
                    fireDocumentChanged();
                }
                @Override
                public void removeUpdate(DocumentEvent e) {
                    fireDocumentChanged();
                }
                private void fireDocumentChanged() {
                    String text = filterField.getText();
                    if (text.isEmpty()) {
                        sorter.setRowFilter(null);
                    } else {
                        try {
                            sorter.setRowFilter(RowFilter.regexFilter(text));
                        } catch (PatternSyntaxException ignore) {
                            // ignore
                        } catch (Exception e) {
                            new ExceptionDialog(e);
                        }
                    }
                }
            });
            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc=" Clear button for filter ">
            final JButton clearTextButton = new JButton();
            clearTextButton.setToolTipText(bundle.getString("filterclear.tooltip"));
            clearTextButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    filterField.setText(EMPTY_STRING);
                    //sorter.setSortKeys(null); // TODO move it to another place
                }
            });
            clearTextButton.addMouseListener(new MouseAdapter() {

                @Override
                public void mouseEntered(MouseEvent e) {
                    clearTextButton.setBorderPainted(true);
                    clearTextButton.setOpaque(true);
                    clearTextButton.setContentAreaFilled(true);
                }

                @Override
                public void mouseExited(MouseEvent e) {
                    clearTextButton.setBorderPainted(false);
                    clearTextButton.setOpaque(false);
                    clearTextButton.setContentAreaFilled(false);
                }
            });
            clearTextButton.setIcon(ImageLoader.getClearIcon());
            clearTextButton.setMaximumSize(clearTextButton.getMinimumSize());
            clearTextButton.setOpaque(false);
            clearTextButton.setBorderPainted(false);
            clearTextButton.setRolloverEnabled(true);
            clearTextButton.setContentAreaFilled(false);
            clearTextButton.setMargin(new Insets(0, 0, 0, 0));
            // </editor-fold>

            // <editor-fold defaultstate="collapsed" desc=" keyListener for table ">
            jtable.addKeyListener(new KeyListener() {

                @Override
                public void keyTyped(KeyEvent e) {
                    // if it is system command - ignore
                    if (e.isAltDown() || e.isMetaDown() || e.getKeyChar() < ' ' || e.getKeyChar() > '~') {
                        return;
                    }

                    // cant set Ctrl+V with actionsMap, so lets check:
                    if (e.isControlDown()) {
                        char eventChar = e.getKeyChar();
                        if (eventChar == 'v' || eventChar == 'V') {
                            new EditDownloadDialog();
                        }
                        return;
                    }

                    // otherwise - focus into filter, and add key pressed to filter
                    filterField.requestFocus();
                    filterField.setText(filterField.getText() + e.getKeyChar());
                }

                @Override
                public void keyPressed(KeyEvent e) {}

                @Override
                public void keyReleased(KeyEvent e) {}
            });
            // </editor-fold>

            Box filterBox = Box.createHorizontalBox();
            DesktopToolkit.setMaxComponentHeight(filterBox);
            filterBox.setMaximumSize(new Dimension(Short.MAX_VALUE, filterBox.getPreferredSize().height));
            filterBox.add(clearTextButton);
            filterBox.add(Box.createHorizontalStrut(3));
            filterBox.add(filterField);
            filterBox.setBorder(BorderFactory.createEmptyBorder(0, 3, 3, 9));

            // create ScrollPane to put into map:
            JScrollPane scrollPane = new JScrollPane(jtable);
            scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            scrollPane.getVerticalScrollBar().addAdjustmentListener(model);

            JButton selectColumnsButton = new JButton(new SelectColumnsAction());
            selectColumnsButton.setMargin(emptyInsets);
            selectColumnsButton.setFocusPainted(false);
            selectColumnsButton.setIconTextGap(0);
            selectColumnsButton.setText(null);
            selectColumnsButton.setIcon(ImageLoader.getColumnsIcon());

            scrollPane.setRowHeaderView(rowNumTable);
            scrollPane.setCorner(ScrollPaneConstants.UPPER_LEFT_CORNER, unsortButton);
            scrollPane.setCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER, selectColumnsButton);
            scrollPane.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(6, 10, 2, 10), scrollPane.getBorder()));

            box = Box.createVerticalBox();
            box.add(scrollPane);
            box.add(filterBox);

            componentsMap.put(TABLE, box);
        }
        return box;
    }

    public JComponent getMainView() {
        JComponent mainView = componentsMap.get(MAIN_VIEW);
        if (mainView == null) {
            mainView = new JPanel(new BorderLayout(), true);

            mainView.setDoubleBuffered(true);
            mainView.add(getMainToolBar(), BorderLayout.NORTH);
            mainView.add(getDownloadsTable(), BorderLayout.CENTER);
            mainView.add(getMainStatusBar(), BorderLayout.SOUTH);

            componentsMap.put(MAIN_VIEW, mainView);
        }
        return mainView;
    }

    public JComponent getMainToolBar() {
        // TODO separate to groups + make floatable
        JToolBar toolbar = (JToolBar)componentsMap.get(TOOLBAR);
        if (toolbar == null) {
            toolbar = new JToolBar();
            toolbar.setRollover(true);
            toolbar.setBorder(new EtchedBorder() {
                @Override
                public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
                    int w = width-1;
                    int h = height-1;

                    g.translate(x, y);

                    g.setColor(getShadowColor(c));
                    g.drawLine(0, 0, w, 0);
                    g.drawLine(0, h-1, w, h-1);

                    g.setColor(getHighlightColor(c));
                    g.drawLine(0, 1, w, 1);
                    g.drawLine(0, h, w, h);

                    g.translate(-x, -y);
                }
            });
            toolbar.setFloatable(false); // TODO check if we will need this

            // <editor-fold defaultstate="collapsed" desc=" Buttons for toolbar ">
            ActionFactory actionFactory = Mediator.getMediator().getActionFactory();
            if (Settings.isDebugMode()) {
                toolbar.add(new GCLabel()); // TODO make it floatable and option to hide it
                toolbar.addSeparator();
            }
            toolbar.add(GUIFactory.createToolbarButton(actionFactory.createSaveListAction()));
            //toolbar.add(GUIFactory.createToolbarButton(actionFactory.createSaveListAsAction()));
            toolbar.add(GUIFactory.createToolbarButton(actionFactory.createSettingsDialogAction()));
            toolbar.addSeparator();
            toolbar.add(GUIFactory.createToolbarButton(actionFactory.createEditElementAction()));
            toolbar.add(GUIFactory.createToolbarButton(actionFactory.createRemoveElementAction()));
            toolbar.add(GUIFactory.createToolbarButton(actionFactory.createAddElementAction()));
            toolbar.addSeparator();
            toolbar.add(GUIFactory.createToolbarButton(actionFactory.createStartAction()));
            toolbar.add(GUIFactory.createToolbarButton(actionFactory.createPauseAction()));
            toolbar.add(GUIFactory.createToolbarButton(actionFactory.createStopAction()));
            // </editor-fold>

            componentsMap.put(TOOLBAR, toolbar);
        }
        return toolbar;
    }

    public JMenuBar getMainMenu() {
        JMenuBar mainMenu = (JMenuBar)componentsMap.get(MAIN_MENU);
        if (mainMenu == null) {
            mainMenu = new JMenuBar();

            ActionFactory actionFactory = Mediator.getMediator().getActionFactory();

            JMenu file = new JMenu(menuBundle.getString("file.menu"));
            file.add(actionFactory.createSaveListAction());
            file.add(actionFactory.createSaveListAsAction());
            file.addSeparator();
            file.add(actionFactory.createExitAction());

            JMenu options = new JMenu(menuBundle.getString("options.menu"));
            options.add(actionFactory.createSettingsDialogAction());

            JMenu downloads = new JMenu(menuBundle.getString("downloads.menu"));
            downloads.add(actionFactory.createAddElementAction());
            downloads.addSeparator();
            downloads.add(actionFactory.createStartAction());
            downloads.add(actionFactory.createPauseAction());
            downloads.add(actionFactory.createStopAction());
            downloads.addSeparator();
            downloads.add(actionFactory.createStartAllAction());
            downloads.add(actionFactory.createStopAllAction());
            downloads.add(actionFactory.createRemoveCompletedAction());

            JMenu window = new JMenu(menuBundle.getString("window.menu"));
            window.add(new JCheckBoxMenuItem(actionFactory.createShowMainWindowAction()));
            window.add(new JCheckBoxMenuItem(actionFactory.createShowDropTargetAction()));

            JMenu help = new JMenu(menuBundle.getString("help.menu"));
            JMenuItem help_info = new JMenuItem("About");
            help.add(new ViewLogAction()); // TODO may be move to action factory?
            help.addSeparator();
            help.add(help_info);

            mainMenu.add(file);
            mainMenu.add(options);
            mainMenu.add(downloads);
            mainMenu.add(window);
            mainMenu.add(help);
            componentsMap.put(MAIN_MENU, mainMenu);
        }
        return mainMenu;
    }

    public MainStatusBar getMainStatusBar() {
        MainStatusBar statusBar = (MainStatusBar)componentsMap.get(MAIN_STATUS_BAR);
        if (statusBar == null) {
            statusBar = new MainStatusBar();
            DesktopToolkit.setMaxComponentHeight(statusBar);
            componentsMap.put(MAIN_STATUS_BAR, statusBar);
        }
        return statusBar;
    }

    public DroptargetStatPanel getDropTargetStatusBar() {
        DroptargetStatPanel statusBar = (DroptargetStatPanel)componentsMap.get(DROP_TARGET_STATUS_BAR);
        if (statusBar == null) {
            statusBar = new DroptargetStatPanel();
            componentsMap.put(DROP_TARGET_STATUS_BAR, statusBar);
        }
        return statusBar;
    }

    public JPopupMenu getPopupMenu() {
        JPopupMenu popupMenu = (JPopupMenu)componentsMap.get(POPUP_MENU);
        if (popupMenu == null) {
            // TODO may be will need more items in menu
            popupMenu = new JPopupMenu();
            ActionFactory actionFactory = Mediator.getMediator().getActionFactory();
            popupMenu.add(actionFactory.createAddElementAction());
            popupMenu.addSeparator();
            popupMenu.add(actionFactory.createStartAction());
            popupMenu.add(actionFactory.createPauseAction());
            popupMenu.add(actionFactory.createStopAction());
            popupMenu.addSeparator();
            popupMenu.add(actionFactory.createRemoveElementAction());
            popupMenu.addSeparator();
            popupMenu.add(actionFactory.createEditElementAction());

            componentsMap.put(POPUP_MENU, popupMenu);
        }
        return popupMenu;
    }

    public Table getTable() {
        Table table = (Table)componentsMap.get(JUST_TABLE);
        if (table == null) {
            table = new Table();
            componentsMap.put(JUST_TABLE, table);

            table.setAutoCreateColumnsFromModel(false);
            table.setModel(model);
            table.setColumnModel(new TableColumnModel());
            table.setAutoResizeMode(Table.AUTO_RESIZE_NEXT_COLUMN);

            table.setDoubleBuffered(true);
            table.setShowGrid(false);
            table.setShowHorizontalLines(false);
            table.setShowVerticalLines(true);
            table.setDragEnabled(true);
            table.setFillsViewportHeight(true);
            table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
            table.setDefaultRenderer(Object.class, new TableCellRenderer());
            table.setTransferHandler(new TableTransferHandler());
            table.addMouseListener(new TableMouseAdapter());
        }
        return table;
    }
    public boolean isTableVisible() {
        return componentsMap.get(JUST_TABLE) != null;
    }

    public SimpleAuthorizationDialog getAuthorizationDialog(){
        //TODO maybe try to use created earlier?
        return new SimpleAuthorizationDialog();
    }

    // </editor-fold>

    private class RowNumTable extends JTable {
        private JTable jtable = null;
        public RowNumTable(JTable table) {
            super();
            jtable = table;
        }
        @Override
        public boolean isCellEditable(int row, int column) {
            return false;
        }
        @Override
        public Object getValueAt(int row, int column) {
            return row + 1;
        }
        @Override
        public int getRowHeight(int row) {
            return jtable.getRowHeight();
        }
        @Override
        public void tableChanged(TableModelEvent e) {
            super.tableChanged(e);
            calculateSize();
        }
        public void calculateSize() {
            int rowCount = getRowCount();
            int times = 1;
            while (rowCount > 9) {
                rowCount = rowCount / 10;
                times++;
            }
            if (getColumnCount() > 0) {
                columnModel.getColumn(0).setPreferredWidth(times * 10);
            }
            setPreferredScrollableViewportSize(getPreferredSize());
        }
    }

    private class TableMouseAdapter extends MouseAdapter {

        private long lastClick = 0;
        private int lastRow = -1;

        @Override
        public void mouseClicked(MouseEvent e) {
            JTable table = (JTable)e.getComponent();
            Point clickPoint = e.getPoint();
            int currentClickRow = table.rowAtPoint(clickPoint);

            // is any modifier key pressed?
            boolean modifierPressed = e.isAltDown() || e.isControlDown() || e.isShiftDown();

            // popup menu?
            if (e.isPopupTrigger() || (!modifierPressed && e.getButton() == MouseEvent.BUTTON3)) {
                // checking if clicked on selected row
                ListSelectionModel selectionModel = table.getSelectionModel();
                if (!selectionModel.isSelectedIndex(currentClickRow)) {
                    // if click performed not on selected row - selecting row
                    selectionModel.setSelectionInterval(currentClickRow, currentClickRow);
                }
                getPopupMenu().show(table, clickPoint.x, clickPoint.y);
                return;
            }
            // if not pupup menu - then just click on table:
            if (!modifierPressed && e.getButton() == MouseEvent.BUTTON1) {
                // TODO think how to merge clearing code in one block without copy
                // if clicked not on any row - clearing selection of table
                if (currentClickRow < 0) {
                    table.getSelectionModel().clearSelection();
                    lastClick = 0;
                    lastRow = -1;
                    return;
                }
                // if last click was performed not later than 0.8 second - it's double click
                long t = e.getWhen();
                if (t - lastClick <= 800 && currentClickRow == lastRow) {
                    new EditDownloadDialog(currentClickRow);
                    lastClick = 0;
                    lastRow = -1;
                    return;
                }
                // just registeing click
                lastClick = t;
                lastRow = currentClickRow;
            }
        }

    }

}
